@useinvio/nfse-sdk 1.0.2 → 2.0.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/README.md +10 -0
- package/dist/certificateValidation.d.ts +7 -0
- package/dist/certificateValidation.js +27 -0
- package/dist/certificateValidation.js.map +1 -0
- package/dist/dpsJson.js +2 -2
- package/dist/dpsJson.js.map +1 -1
- package/dist/emissaoNota.d.ts +9 -0
- package/dist/emissaoNota.js +34 -24
- package/dist/emissaoNota.js.map +1 -1
- package/dist/fiscalValidation.d.ts +26 -3
- package/dist/fiscalValidation.js +159 -10
- package/dist/fiscalValidation.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/sefinClient.d.ts +50 -0
- package/dist/sefinClient.js +136 -10
- package/dist/sefinClient.js.map +1 -1
- package/dist/xsdValidation.d.ts +6 -0
- package/dist/xsdValidation.js +46 -0
- package/dist/xsdValidation.js.map +1 -0
- package/docs/METRICS.md +249 -0
- package/llms.txt +80 -0
- package/package.json +5 -2
- package/schemas/nfse/v1.01/Schemas/1.00/CNC_v1.00.xsd +13 -0
- package/schemas/nfse/v1.01/Schemas/1.00/DPS_v1.00.xsd +13 -0
- package/schemas/nfse/v1.01/Schemas/1.00/NFSe_v1.00.xsd +14 -0
- package/schemas/nfse/v1.01/Schemas/1.00/evento_v1.00.xsd +15 -0
- package/schemas/nfse/v1.01/Schemas/1.00/pedRegEvento_v1.00.xsd +15 -0
- package/schemas/nfse/v1.01/Schemas/1.00/tiposCnc_v1.00.xsd +181 -0
- package/schemas/nfse/v1.01/Schemas/1.00/tiposComplexos_v1.00.xsd +1828 -0
- package/schemas/nfse/v1.01/Schemas/1.00/tiposEventos_v1.00.xsd +733 -0
- package/schemas/nfse/v1.01/Schemas/1.00/tiposSimples_v1.00.xsd +1658 -0
- package/schemas/nfse/v1.01/Schemas/1.00/xmldsig-core-schema.xsd +92 -0
- package/schemas/nfse/v1.01/Schemas/1.01/CNC_v1.00.xsd +14 -0
- package/schemas/nfse/v1.01/Schemas/1.01/DPS_v1.01.xsd +14 -0
- package/schemas/nfse/v1.01/Schemas/1.01/NFSe_v1.01.xsd +15 -0
- package/schemas/nfse/v1.01/Schemas/1.01/evento_v1.01.xsd +15 -0
- package/schemas/nfse/v1.01/Schemas/1.01/pedRegEvento_v1.01.xsd +15 -0
- package/schemas/nfse/v1.01/Schemas/1.01/tiposCnc_v1.00.xsd +166 -0
- package/schemas/nfse/v1.01/Schemas/1.01/tiposComplexos_v1.01.xsd +2709 -0
- package/schemas/nfse/v1.01/Schemas/1.01/tiposEventos_v1.01.xsd +780 -0
- package/schemas/nfse/v1.01/Schemas/1.01/tiposSimples_v1.01.xsd +1844 -0
- package/schemas/nfse/v1.01/Schemas/1.01/xmldsig-core-schema.xsd +318 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAG5F,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAG5F,OAAO,EACL,aAAa,EACb,yBAAyB,EACzB,YAAY,EACZ,YAAY,EACZ,uBAAuB,EACvB,4BAA4B,EAC5B,yBAAyB,EACzB,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAa1B,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAmB5D,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAC;AAQ/B,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAGtG,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAUzC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAExD,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEzF,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAErF,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAG1D,OAAO,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC"}
|
package/dist/sefinClient.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { PfxMaterial } from './loadPfx.js';
|
|
2
2
|
import type { Ambiente } from './config.js';
|
|
3
|
+
export declare const SEFIN_LATENCY_METRICS_ENV = "NFSE_SEFIN_LATENCY_METRICS";
|
|
3
4
|
export interface SefinErro {
|
|
4
5
|
Codigo: string;
|
|
5
6
|
Descricao: string;
|
|
@@ -10,6 +11,55 @@ export interface SefinResposta {
|
|
|
10
11
|
/** Corpo bruto parseado (JSON) */
|
|
11
12
|
body: any;
|
|
12
13
|
}
|
|
14
|
+
export type SefinOperation = 'transmitir_dps' | 'consultar_nfse' | 'enviar_evento';
|
|
15
|
+
export interface SefinRequestMetric {
|
|
16
|
+
operation: SefinOperation;
|
|
17
|
+
ambiente?: Ambiente;
|
|
18
|
+
method: 'GET' | 'POST';
|
|
19
|
+
pathTemplate: string;
|
|
20
|
+
durationMs: number;
|
|
21
|
+
status?: number;
|
|
22
|
+
success: boolean;
|
|
23
|
+
errorName?: string;
|
|
24
|
+
errorMessage?: string;
|
|
25
|
+
}
|
|
26
|
+
export type SefinRequestObserver = (metric: SefinRequestMetric) => void;
|
|
27
|
+
export interface SefinLatencyTrackerOptions {
|
|
28
|
+
/**
|
|
29
|
+
* Maximum number of recent latency samples kept for each operation/environment
|
|
30
|
+
* series. Min, max, average, and optional percentiles use this rolling window.
|
|
31
|
+
*/
|
|
32
|
+
maxSamplesPerSeries?: number;
|
|
33
|
+
/** Percentile metrics are intentionally opt-in because they keep extra series in exporters. */
|
|
34
|
+
includePercentiles?: boolean;
|
|
35
|
+
}
|
|
36
|
+
export interface SefinLatencySeriesSnapshot {
|
|
37
|
+
operation: SefinOperation;
|
|
38
|
+
ambiente?: Ambiente;
|
|
39
|
+
count: number;
|
|
40
|
+
successCount: number;
|
|
41
|
+
errorCount: number;
|
|
42
|
+
sampleCount: number;
|
|
43
|
+
minMs: number;
|
|
44
|
+
maxMs: number;
|
|
45
|
+
avgMs: number;
|
|
46
|
+
p50Ms?: number;
|
|
47
|
+
p95Ms?: number;
|
|
48
|
+
p99Ms?: number;
|
|
49
|
+
}
|
|
50
|
+
export interface SefinLatencySnapshot {
|
|
51
|
+
generatedAt: string;
|
|
52
|
+
series: SefinLatencySeriesSnapshot[];
|
|
53
|
+
}
|
|
54
|
+
export interface SefinLatencyTracker {
|
|
55
|
+
observe(metric: SefinRequestMetric): void;
|
|
56
|
+
snapshot(): SefinLatencySnapshot;
|
|
57
|
+
reset(): void;
|
|
58
|
+
}
|
|
59
|
+
export declare function isSefinLatencyMetricsEnabled(env?: NodeJS.ProcessEnv): boolean;
|
|
60
|
+
export declare function setSefinRequestObserver(observer: SefinRequestObserver | undefined): void;
|
|
61
|
+
export declare function getSefinRequestObserver(): SefinRequestObserver | undefined;
|
|
62
|
+
export declare function createSefinLatencyTracker(options?: SefinLatencyTrackerOptions): SefinLatencyTracker;
|
|
13
63
|
/** Normaliza a lista de erros retornada pelo SEFIN em diferentes formatos. */
|
|
14
64
|
export declare function extrairErros(body: any): SefinErro[];
|
|
15
65
|
/** SefinNacional · POST /nfse — transporte baixo nivel de DPS ja compactada em GZip+Base64. */
|
package/dist/sefinClient.js
CHANGED
|
@@ -1,12 +1,105 @@
|
|
|
1
1
|
import https from 'node:https';
|
|
2
|
+
import { performance } from 'node:perf_hooks';
|
|
2
3
|
import { resolveSefinBaseUrl } from './config.js';
|
|
3
4
|
/** Evita que uma conexao travada com o SEFIN prenda um worker indefinidamente. */
|
|
4
5
|
const SEFIN_REQUEST_TIMEOUT_MS = 60_000;
|
|
6
|
+
export const SEFIN_LATENCY_METRICS_ENV = 'NFSE_SEFIN_LATENCY_METRICS';
|
|
7
|
+
let sefinRequestObserver;
|
|
8
|
+
export function isSefinLatencyMetricsEnabled(env = process.env) {
|
|
9
|
+
const value = env[SEFIN_LATENCY_METRICS_ENV]?.trim().toLowerCase();
|
|
10
|
+
return value === '1' || value === 'true' || value === 'yes' || value === 'on';
|
|
11
|
+
}
|
|
12
|
+
export function setSefinRequestObserver(observer) {
|
|
13
|
+
sefinRequestObserver = observer;
|
|
14
|
+
}
|
|
15
|
+
export function getSefinRequestObserver() {
|
|
16
|
+
return sefinRequestObserver;
|
|
17
|
+
}
|
|
18
|
+
function notifySefinRequestObserver(metric) {
|
|
19
|
+
if (!isSefinLatencyMetricsEnabled())
|
|
20
|
+
return;
|
|
21
|
+
try {
|
|
22
|
+
sefinRequestObserver?.(metric);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// Observability hooks must not change SDK transport behavior.
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function percentile(sortedValues, quantile) {
|
|
29
|
+
if (!sortedValues.length)
|
|
30
|
+
return 0;
|
|
31
|
+
const index = Math.min(sortedValues.length - 1, Math.max(0, Math.ceil(sortedValues.length * quantile) - 1));
|
|
32
|
+
return sortedValues[index];
|
|
33
|
+
}
|
|
34
|
+
function trackerKey(metric) {
|
|
35
|
+
return `${metric.operation}:${metric.ambiente ?? 'default'}`;
|
|
36
|
+
}
|
|
37
|
+
export function createSefinLatencyTracker(options = {}) {
|
|
38
|
+
const maxSamplesPerSeries = Math.max(1, Math.floor(options.maxSamplesPerSeries ?? 1_000));
|
|
39
|
+
const includePercentiles = options.includePercentiles === true;
|
|
40
|
+
const series = new Map();
|
|
41
|
+
return {
|
|
42
|
+
observe(metric) {
|
|
43
|
+
const key = trackerKey(metric);
|
|
44
|
+
let bucket = series.get(key);
|
|
45
|
+
if (!bucket) {
|
|
46
|
+
bucket = {
|
|
47
|
+
operation: metric.operation,
|
|
48
|
+
ambiente: metric.ambiente,
|
|
49
|
+
count: 0,
|
|
50
|
+
successCount: 0,
|
|
51
|
+
errorCount: 0,
|
|
52
|
+
durations: [],
|
|
53
|
+
};
|
|
54
|
+
series.set(key, bucket);
|
|
55
|
+
}
|
|
56
|
+
bucket.count += 1;
|
|
57
|
+
if (metric.success)
|
|
58
|
+
bucket.successCount += 1;
|
|
59
|
+
else
|
|
60
|
+
bucket.errorCount += 1;
|
|
61
|
+
bucket.durations.push(metric.durationMs);
|
|
62
|
+
if (bucket.durations.length > maxSamplesPerSeries) {
|
|
63
|
+
bucket.durations.splice(0, bucket.durations.length - maxSamplesPerSeries);
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
snapshot() {
|
|
67
|
+
return {
|
|
68
|
+
generatedAt: new Date().toISOString(),
|
|
69
|
+
series: Array.from(series.values()).map((bucket) => {
|
|
70
|
+
const sortedDurations = [...bucket.durations].sort((a, b) => a - b);
|
|
71
|
+
const total = sortedDurations.reduce((sum, value) => sum + value, 0);
|
|
72
|
+
const snapshot = {
|
|
73
|
+
operation: bucket.operation,
|
|
74
|
+
ambiente: bucket.ambiente,
|
|
75
|
+
count: bucket.count,
|
|
76
|
+
successCount: bucket.successCount,
|
|
77
|
+
errorCount: bucket.errorCount,
|
|
78
|
+
sampleCount: sortedDurations.length,
|
|
79
|
+
minMs: sortedDurations[0] ?? 0,
|
|
80
|
+
maxMs: sortedDurations.at(-1) ?? 0,
|
|
81
|
+
avgMs: sortedDurations.length ? total / sortedDurations.length : 0,
|
|
82
|
+
};
|
|
83
|
+
if (includePercentiles) {
|
|
84
|
+
snapshot.p50Ms = percentile(sortedDurations, 0.50);
|
|
85
|
+
snapshot.p95Ms = percentile(sortedDurations, 0.95);
|
|
86
|
+
snapshot.p99Ms = percentile(sortedDurations, 0.99);
|
|
87
|
+
}
|
|
88
|
+
return snapshot;
|
|
89
|
+
}).sort((a, b) => (a.operation.localeCompare(b.operation) ||
|
|
90
|
+
(a.ambiente ?? '').localeCompare(b.ambiente ?? ''))),
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
reset() {
|
|
94
|
+
series.clear();
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
5
98
|
/**
|
|
6
99
|
* Requisição mTLS de baixo nível ao SefinNacional, retornando o corpo bruto (Buffer).
|
|
7
100
|
* Usa key+cert PEM no handshake (o .pfx legado ICP-Brasil é rejeitado pelo TLS do Node).
|
|
8
101
|
*/
|
|
9
|
-
function sefinRequest(opts, pfx, baseOverride) {
|
|
102
|
+
function sefinRequest(opts, pfx, metricOpts, baseOverride) {
|
|
10
103
|
const url = new URL(`${baseOverride ?? resolveSefinBaseUrl()}${opts.path}`);
|
|
11
104
|
const payload = opts.jsonBody != null ? JSON.stringify(opts.jsonBody) : undefined;
|
|
12
105
|
const headers = {
|
|
@@ -28,16 +121,49 @@ function sefinRequest(opts, pfx, baseOverride) {
|
|
|
28
121
|
timeout: SEFIN_REQUEST_TIMEOUT_MS,
|
|
29
122
|
};
|
|
30
123
|
return new Promise((resolve, reject) => {
|
|
124
|
+
const startedAt = performance.now();
|
|
125
|
+
let metricSent = false;
|
|
126
|
+
const emitMetric = (metric) => {
|
|
127
|
+
if (metricSent)
|
|
128
|
+
return;
|
|
129
|
+
metricSent = true;
|
|
130
|
+
notifySefinRequestObserver({
|
|
131
|
+
...metric,
|
|
132
|
+
durationMs: performance.now() - startedAt,
|
|
133
|
+
});
|
|
134
|
+
};
|
|
31
135
|
const req = https.request(options, (res) => {
|
|
32
136
|
const chunks = [];
|
|
33
137
|
res.on('data', (c) => chunks.push(c));
|
|
34
|
-
res.on('end', () =>
|
|
35
|
-
status
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
138
|
+
res.on('end', () => {
|
|
139
|
+
const status = res.statusCode ?? 0;
|
|
140
|
+
emitMetric({
|
|
141
|
+
operation: metricOpts.operation,
|
|
142
|
+
ambiente: metricOpts.ambiente,
|
|
143
|
+
method: opts.method,
|
|
144
|
+
pathTemplate: metricOpts.pathTemplate,
|
|
145
|
+
status,
|
|
146
|
+
success: status >= 200 && status < 300,
|
|
147
|
+
});
|
|
148
|
+
resolve({
|
|
149
|
+
status,
|
|
150
|
+
contentType: String(res.headers['content-type'] ?? ''),
|
|
151
|
+
buffer: Buffer.concat(chunks),
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
req.on('error', (error) => {
|
|
156
|
+
emitMetric({
|
|
157
|
+
operation: metricOpts.operation,
|
|
158
|
+
ambiente: metricOpts.ambiente,
|
|
159
|
+
method: opts.method,
|
|
160
|
+
pathTemplate: metricOpts.pathTemplate,
|
|
161
|
+
success: false,
|
|
162
|
+
errorName: error.name,
|
|
163
|
+
errorMessage: error.message,
|
|
164
|
+
});
|
|
165
|
+
reject(error);
|
|
39
166
|
});
|
|
40
|
-
req.on('error', reject);
|
|
41
167
|
req.on('timeout', () => {
|
|
42
168
|
req.destroy(new Error(`SEFIN request timed out after ${SEFIN_REQUEST_TIMEOUT_MS}ms`));
|
|
43
169
|
});
|
|
@@ -104,12 +230,12 @@ export function extrairErros(body) {
|
|
|
104
230
|
/** SefinNacional · POST /nfse — transporte baixo nivel de DPS ja compactada em GZip+Base64. */
|
|
105
231
|
export async function transmitirDpsCompactada(dpsXmlGZipB64, pfx, ambiente) {
|
|
106
232
|
const base = ambiente ? resolveSefinBaseUrl(ambiente) : undefined;
|
|
107
|
-
return parseJson(await sefinRequest({ method: 'POST', path: '/nfse', jsonBody: { dpsXmlGZipB64 } }, pfx, base));
|
|
233
|
+
return parseJson(await sefinRequest({ method: 'POST', path: '/nfse', jsonBody: { dpsXmlGZipB64 } }, pfx, { operation: 'transmitir_dps', ambiente, pathTemplate: '/nfse' }, base));
|
|
108
234
|
}
|
|
109
235
|
/** SefinNacional · GET /nfse/{chave} — consulta a NFS-e pela chave de acesso (50 dígitos). */
|
|
110
236
|
export async function consultarNfse(chave, pfx, ambiente) {
|
|
111
237
|
const base = ambiente ? resolveSefinBaseUrl(ambiente) : undefined;
|
|
112
|
-
return parseJson(await sefinRequest({ method: 'GET', path: `/nfse/${chave}` }, pfx, base));
|
|
238
|
+
return parseJson(await sefinRequest({ method: 'GET', path: `/nfse/${chave}` }, pfx, { operation: 'consultar_nfse', ambiente, pathTemplate: '/nfse/{chave}' }, base));
|
|
113
239
|
}
|
|
114
240
|
/** SefinNacional · POST /nfse/{chave}/eventos — registra evento fiscal da NFS-e. */
|
|
115
241
|
export async function enviarEvento(pedRegXmlGZipB64, pfx, chaveAcesso, ambiente) {
|
|
@@ -118,6 +244,6 @@ export async function enviarEvento(pedRegXmlGZipB64, pfx, chaveAcesso, ambiente)
|
|
|
118
244
|
method: 'POST',
|
|
119
245
|
path: `/nfse/${chaveAcesso}/eventos`,
|
|
120
246
|
jsonBody: { pedRegXmlGZipB64 },
|
|
121
|
-
}, pfx, base));
|
|
247
|
+
}, pfx, { operation: 'enviar_evento', ambiente, pathTemplate: '/nfse/{chave}/eventos' }, base));
|
|
122
248
|
}
|
|
123
249
|
//# sourceMappingURL=sefinClient.js.map
|
package/dist/sefinClient.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sefinClient.js","sourceRoot":"","sources":["../sefinClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAIlD,kFAAkF;AAClF,MAAM,wBAAwB,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"sefinClient.js","sourceRoot":"","sources":["../sefinClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare class DpsXsdValidationError extends Error {
|
|
2
|
+
readonly details: string[];
|
|
3
|
+
constructor(details: string[]);
|
|
4
|
+
}
|
|
5
|
+
/** Validates a generated DPS against the official v1.01 schema before signing. */
|
|
6
|
+
export declare function validateDpsXmlAgainstXsd(xml: string): void;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { cpSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
export class DpsXsdValidationError extends Error {
|
|
7
|
+
details;
|
|
8
|
+
constructor(details) {
|
|
9
|
+
super(`XML da DPS invalido para o XSD v1.01: ${details.join('; ')}`);
|
|
10
|
+
this.details = details;
|
|
11
|
+
this.name = 'DpsXsdValidationError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function schemaSourceDir() {
|
|
15
|
+
return fileURLToPath(new URL('../schemas/nfse/v1.01/Schemas/1.01/', import.meta.url));
|
|
16
|
+
}
|
|
17
|
+
/** Validates a generated DPS against the official v1.01 schema before signing. */
|
|
18
|
+
export function validateDpsXmlAgainstXsd(xml) {
|
|
19
|
+
const workDir = mkdtempSync(join(tmpdir(), 'nfse-sdk-xsd-'));
|
|
20
|
+
const schemasDir = join(workDir, 'schemas');
|
|
21
|
+
try {
|
|
22
|
+
cpSync(schemaSourceDir(), schemasDir, { recursive: true });
|
|
23
|
+
// libxml2 treats ^/$ literally in XSD regexes. The official package ships
|
|
24
|
+
// one JavaScript-style anchor, so normalize that expression in our copy.
|
|
25
|
+
const simpleTypesPath = join(schemasDir, 'tiposSimples_v1.01.xsd');
|
|
26
|
+
const simpleTypes = readFileSync(simpleTypesPath, 'utf8').replace('<xs:pattern value="^0{0,4}\\d{1,5}$"/>', '<xs:pattern value="0{0,4}\\d{1,5}"/>');
|
|
27
|
+
writeFileSync(simpleTypesPath, simpleTypes);
|
|
28
|
+
const xmlPath = join(workDir, 'dps.xml');
|
|
29
|
+
writeFileSync(xmlPath, xml);
|
|
30
|
+
try {
|
|
31
|
+
execFileSync('xmllint', ['--noout', '--schema', join(schemasDir, 'DPS_v1.01.xsd'), xmlPath], {
|
|
32
|
+
encoding: 'utf8',
|
|
33
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const stderr = error && typeof error === 'object' && 'stderr' in error ? String(error.stderr) : String(error);
|
|
38
|
+
const details = stderr.split('\n').map((line) => line.trim()).filter(Boolean);
|
|
39
|
+
throw new DpsXsdValidationError(details.length ? details : ['Falha desconhecida ao validar o XSD.']);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
rmSync(workDir, { recursive: true, force: true });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=xsdValidation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xsdValidation.js","sourceRoot":"","sources":["../xsdValidation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnF,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAClB;IAA5B,YAA4B,OAAiB;QAC3C,KAAK,CAAC,yCAAyC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAD3C,YAAO,GAAP,OAAO,CAAU;QAE3C,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AAED,SAAS,eAAe;IACtB,OAAO,aAAa,CAAC,IAAI,GAAG,CAAC,qCAAqC,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACxF,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,wBAAwB,CAAC,GAAW;IAClD,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC;IAC7D,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAC5C,IAAI,CAAC;QACH,MAAM,CAAC,eAAe,EAAE,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE3D,0EAA0E;QAC1E,yEAAyE;QACzE,MAAM,eAAe,GAAG,IAAI,CAAC,UAAU,EAAE,wBAAwB,CAAC,CAAC;QACnE,MAAM,WAAW,GAAG,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,OAAO,CAC/D,wCAAwC,EACxC,sCAAsC,CACvC,CAAC;QACF,aAAa,CAAC,eAAe,EAAE,WAAW,CAAC,CAAC;QAE5C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QACzC,aAAa,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC5B,IAAI,CAAC;YACH,YAAY,CAAC,SAAS,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,EAAE,OAAO,CAAC,EAAE;gBAC3F,QAAQ,EAAE,MAAM;gBAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;aAClC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,MAAM,GAAG,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,QAAQ,IAAI,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC9G,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC9E,MAAM,IAAI,qBAAqB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,sCAAsC,CAAC,CAAC,CAAC;QACvG,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;AACH,CAAC"}
|
package/docs/METRICS.md
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# Metricas da SEFIN
|
|
2
|
+
|
|
3
|
+
O SDK pode medir a latencia das chamadas mTLS feitas contra a SEFIN Nacional. A coleta fica desligada por padrao e deve ser habilitada com a variavel de ambiente `NFSE_SEFIN_LATENCY_METRICS=1`. Essas metricas ajudam a responder perguntas como:
|
|
4
|
+
|
|
5
|
+
- qual e o P99 de `POST /nfse` no ambiente restrito?
|
|
6
|
+
- as consultas por chave estao ficando lentas?
|
|
7
|
+
- quantas chamadas externas falharam no processo atual?
|
|
8
|
+
- a lentidao vem da SEFIN ou da minha API?
|
|
9
|
+
|
|
10
|
+
As metricas sao emitidas pelo SDK apenas quando `NFSE_SEFIN_LATENCY_METRICS` esta habilitada, e nao sao enviadas para nenhum servico automaticamente. A aplicacao que usa o SDK decide se quer guardar em memoria, registrar em logs, exportar para Prometheus, enviar para Datadog/New Relic/Grafana, ou expor em um endpoint interno.
|
|
11
|
+
|
|
12
|
+
## O que e medido
|
|
13
|
+
|
|
14
|
+
Cada chamada externa feita pelo `sefinClient.ts` pode emitir um evento com:
|
|
15
|
+
|
|
16
|
+
| Campo | Descricao |
|
|
17
|
+
|---|---|
|
|
18
|
+
| `operation` | Operacao logica do SDK. |
|
|
19
|
+
| `ambiente` | `restrita` ou `producao`, quando informado. |
|
|
20
|
+
| `method` | Metodo HTTP usado contra a SEFIN. |
|
|
21
|
+
| `pathTemplate` | Caminho sem dados sensiveis, por exemplo `/nfse/{chave}`. |
|
|
22
|
+
| `durationMs` | Tempo total do round-trip externo em milissegundos. |
|
|
23
|
+
| `status` | Status HTTP retornado pela SEFIN, quando houve resposta. |
|
|
24
|
+
| `success` | `true` para HTTP 2xx; `false` para HTTP nao 2xx, timeout ou erro de rede. |
|
|
25
|
+
| `errorName` | Nome do erro local, quando a chamada falha antes de uma resposta HTTP. |
|
|
26
|
+
| `errorMessage` | Mensagem do erro local, quando aplicavel. |
|
|
27
|
+
|
|
28
|
+
Operacoes emitidas:
|
|
29
|
+
|
|
30
|
+
| Operacao | Endpoint SEFIN |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `transmitir_dps` | `POST /nfse` |
|
|
33
|
+
| `consultar_nfse` | `GET /nfse/{chave}` |
|
|
34
|
+
| `enviar_evento` | `POST /nfse/{chave}/eventos` |
|
|
35
|
+
|
|
36
|
+
## O que nao e medido
|
|
37
|
+
|
|
38
|
+
Essas metricas nao medem a latencia total da sua API. Elas medem apenas a dependencia externa SEFIN.
|
|
39
|
+
|
|
40
|
+
Exemplo:
|
|
41
|
+
|
|
42
|
+
```txt
|
|
43
|
+
Cliente -> sua API: 20ms
|
|
44
|
+
sua API -> SEFIN -> sua API: 4200ms
|
|
45
|
+
sua API -> banco/logs/outros: 80ms
|
|
46
|
+
latencia total da sua API: 4300ms
|
|
47
|
+
latencia SEFIN medida pelo SDK: 4200ms
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Tambem nao ha persistencia automatica. O agregador em memoria zera quando o processo Node reinicia.
|
|
51
|
+
|
|
52
|
+
## Habilitando a coleta
|
|
53
|
+
|
|
54
|
+
Configure a variavel de ambiente no processo que usa o SDK:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
NFSE_SEFIN_LATENCY_METRICS=1
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Valores aceitos para habilitar: `1`, `true`, `yes` e `on`. Sem essa variavel, ou com outro valor, o SDK nao chama o observer de metricas.
|
|
61
|
+
|
|
62
|
+
## Uso basico
|
|
63
|
+
|
|
64
|
+
Registre um observer uma vez na inicializacao da aplicacao, antes de fazer chamadas como `emitirNfse`, `consultarNfse` ou `enviarEvento`.
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import {
|
|
68
|
+
createSefinLatencyTracker,
|
|
69
|
+
setSefinRequestObserver,
|
|
70
|
+
} from '@useinvio/nfse-sdk';
|
|
71
|
+
|
|
72
|
+
export const sefinLatency = createSefinLatencyTracker({
|
|
73
|
+
maxSamplesPerSeries: 1000,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
setSefinRequestObserver((metric) => {
|
|
77
|
+
sefinLatency.observe(metric);
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Depois, leia o snapshot onde fizer sentido para sua aplicacao:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
const snapshot = sefinLatency.snapshot();
|
|
85
|
+
|
|
86
|
+
console.log(snapshot.series);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Exemplo de retorno:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
{
|
|
93
|
+
generatedAt: '2026-07-09T12:00:00.000Z',
|
|
94
|
+
series: [
|
|
95
|
+
{
|
|
96
|
+
operation: 'transmitir_dps',
|
|
97
|
+
ambiente: 'restrita',
|
|
98
|
+
count: 120,
|
|
99
|
+
successCount: 118,
|
|
100
|
+
errorCount: 2,
|
|
101
|
+
sampleCount: 120,
|
|
102
|
+
minMs: 180,
|
|
103
|
+
maxMs: 8100,
|
|
104
|
+
avgMs: 940.2
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Percentis opcionais
|
|
111
|
+
|
|
112
|
+
Metricas de percentil ficam desligadas por padrao. Para incluir `p50Ms`, `p95Ms` e `p99Ms` no snapshot, habilite explicitamente `includePercentiles`:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
export const sefinLatency = createSefinLatencyTracker({
|
|
116
|
+
maxSamplesPerSeries: 1000,
|
|
117
|
+
includePercentiles: true,
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Com percentis habilitados, o snapshot inclui os campos extras:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
{
|
|
125
|
+
generatedAt: '2026-07-09T12:00:00.000Z',
|
|
126
|
+
series: [
|
|
127
|
+
{
|
|
128
|
+
operation: 'transmitir_dps',
|
|
129
|
+
ambiente: 'restrita',
|
|
130
|
+
count: 120,
|
|
131
|
+
successCount: 118,
|
|
132
|
+
errorCount: 2,
|
|
133
|
+
sampleCount: 120,
|
|
134
|
+
minMs: 180,
|
|
135
|
+
maxMs: 8100,
|
|
136
|
+
avgMs: 940.2,
|
|
137
|
+
p50Ms: 430,
|
|
138
|
+
p95Ms: 2200,
|
|
139
|
+
p99Ms: 5100
|
|
140
|
+
}
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Como interpretar P99
|
|
146
|
+
|
|
147
|
+
`p99Ms` significa que 99% das chamadas observadas naquela serie foram mais rapidas ou iguais a esse valor.
|
|
148
|
+
|
|
149
|
+
Se `transmitir_dps` em `restrita` tem `p99Ms = 5100`, a leitura pratica e:
|
|
150
|
+
|
|
151
|
+
```txt
|
|
152
|
+
99% das transmissoes de DPS para a SEFIN restrita responderam em ate 5100ms.
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Um `p99Ms` alto com `p50Ms` baixo indica cauda longa: a maioria das chamadas e rapida, mas uma pequena parte fica muito lenta. Em integracoes fiscais isso costuma aparecer por fila, instabilidade, timeout, validacao pesada ou oscilacao da dependencia externa.
|
|
156
|
+
|
|
157
|
+
## Series
|
|
158
|
+
|
|
159
|
+
O tracker agrega por:
|
|
160
|
+
|
|
161
|
+
```txt
|
|
162
|
+
operation + ambiente
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Assim, `transmitir_dps` em `restrita` nao se mistura com `consultar_nfse` em `producao`.
|
|
166
|
+
|
|
167
|
+
## Janela movel
|
|
168
|
+
|
|
169
|
+
`maxSamplesPerSeries` controla quantas duracoes recentes ficam guardadas por serie.
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
const sefinLatency = createSefinLatencyTracker({
|
|
173
|
+
maxSamplesPerSeries: 5000,
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Com `5000`, cada combinacao `operation + ambiente` calcula `minMs`, `maxMs`, `avgMs` e, quando habilitados, os percentis usando no maximo as ultimas 5000 amostras daquela serie. O contador `count` continua acumulando o total observado desde o ultimo `reset`.
|
|
178
|
+
|
|
179
|
+
## Logs estruturados
|
|
180
|
+
|
|
181
|
+
Voce pode registrar cada chamada em log e tambem alimentar o tracker:
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
setSefinRequestObserver((metric) => {
|
|
185
|
+
sefinLatency.observe(metric);
|
|
186
|
+
|
|
187
|
+
logger.info({
|
|
188
|
+
event: 'sefin_request',
|
|
189
|
+
operation: metric.operation,
|
|
190
|
+
ambiente: metric.ambiente,
|
|
191
|
+
status: metric.status,
|
|
192
|
+
success: metric.success,
|
|
193
|
+
durationMs: metric.durationMs,
|
|
194
|
+
errorName: metric.errorName,
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Evite registrar payload fiscal, XML, certificado, chave privada ou dados sensiveis. O SDK ja usa `pathTemplate` para evitar colocar a chave de acesso no nome da serie.
|
|
200
|
+
|
|
201
|
+
## Endpoint interno na sua API
|
|
202
|
+
|
|
203
|
+
Se quiser consultar essas metricas pela sua API, crie um endpoint protegido que retorne o snapshot:
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
import { sefinLatency } from './observability/sefinMetrics.js';
|
|
207
|
+
|
|
208
|
+
app.get('/internal/metrics/sefin', async (_request, reply) => {
|
|
209
|
+
return reply.send(sefinLatency.snapshot());
|
|
210
|
+
});
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Esse endpoint deve ser interno ou protegido por autenticacao forte. As metricas nao carregam XML nem certificado, mas podem revelar volume, estabilidade e comportamento operacional do seu sistema.
|
|
214
|
+
|
|
215
|
+
## Prometheus e outros coletores
|
|
216
|
+
|
|
217
|
+
O SDK nao depende de Prometheus. Para Prometheus, voce pode converter o snapshot para o formato esperado pela sua biblioteca de metricas.
|
|
218
|
+
|
|
219
|
+
Exemplo conceitual:
|
|
220
|
+
|
|
221
|
+
```txt
|
|
222
|
+
sefin_request_duration_avg_ms{operation="transmitir_dps",ambiente="restrita"} 940.2
|
|
223
|
+
sefin_request_success_total{operation="transmitir_dps",ambiente="restrita"} 118
|
|
224
|
+
sefin_request_error_total{operation="transmitir_dps",ambiente="restrita"} 2
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Exporte `sefin_request_duration_p99_ms` apenas quando `includePercentiles: true` estiver habilitado.
|
|
228
|
+
|
|
229
|
+
Se voce ja usa OpenTelemetry, Datadog, New Relic, Grafana Cloud ou outro coletor, use `setSefinRequestObserver` como ponto de saida e envie `durationMs`, `operation`, `ambiente`, `status` e `success`.
|
|
230
|
+
|
|
231
|
+
## Reset
|
|
232
|
+
|
|
233
|
+
O tracker pode ser zerado:
|
|
234
|
+
|
|
235
|
+
```ts
|
|
236
|
+
sefinLatency.reset();
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Isso remove as series e amostras em memoria. Use com cuidado em producao, porque o snapshot perde a janela atual de percentis.
|
|
240
|
+
|
|
241
|
+
## Cuidados
|
|
242
|
+
|
|
243
|
+
- Registre o observer uma unica vez por processo.
|
|
244
|
+
- Nao crie um tracker novo a cada request HTTP.
|
|
245
|
+
- Use `maxSamplesPerSeries` conforme o volume esperado e a memoria disponivel.
|
|
246
|
+
- Habilite `includePercentiles` apenas quando sua operacao realmente precisar de P50/P95/P99.
|
|
247
|
+
- Lembre que processos diferentes tem snapshots diferentes. Em varios containers/replicas, cada processo enxerga apenas as chamadas que ele executou.
|
|
248
|
+
- Para historico entre deploys/restarts, envie as metricas para um sistema externo.
|
|
249
|
+
- Para saber os erros oficiais mais comuns da SEFIN, use a resposta de negocio e `extrairErros(body)` no seu fluxo. As metricas de latencia focam em tempo, status e sucesso/falha da chamada externa.
|