nf-key-extractor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 devAlphaSystem
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,427 @@
1
+ # nf-key-extractor
2
+
3
+ Extração local, somente por CPU, de chaves de acesso validadas de NF-e e NFC-e
4
+ em arquivos PDF locais ou remotos. Primeiro, verifica o texto nativo do PDF;
5
+ depois, Code 128/QR; e usa OCR local somente quando o perfil selecionado permite.
6
+ Os arquivos remotos são baixados para o processo Node.js; a análise do PDF, o
7
+ reconhecimento de códigos de barras e o OCR permanecem locais.
8
+
9
+ O pacote aceita tanto as chaves numéricas legadas de 44 dígitos quanto o formato
10
+ atual de 44 caracteres, introduzido para CNPJs alfanuméricos em julho de 2026.
11
+
12
+ ## Instalação
13
+
14
+ ```bash
15
+ npm install nf-key-extractor
16
+ ```
17
+
18
+ É necessário usar Node.js 20 ou mais recente. Não é preciso instalar o Tesseract
19
+ no sistema, usar GPU ou serviço externo de extração, baixar modelos durante a
20
+ execução nem fornecer credenciais de API ao pacote. A aplicação que chama o
21
+ pacote pode fornecer credenciais quando a URL do PDF exigir autenticação.
22
+
23
+ ## Uma chamada de função
24
+
25
+ ```ts
26
+ import { extractNFeAccessKeys } from "nf-key-extractor";
27
+
28
+ const result = await extractNFeAccessKeys("/local/path/danfe.pdf", {
29
+ performance: "balanced",
30
+ passes: 2,
31
+ });
32
+
33
+ console.log(JSON.stringify(result, null, 2));
34
+ ```
35
+
36
+ Também são aceitos `Buffer`, `Uint8Array` e `ArrayBuffer`:
37
+
38
+ ```ts
39
+ const result = await extractNFeAccessKeys(pdfBuffer);
40
+ ```
41
+
42
+ URLs públicas HTTP/HTTPS de PDFs podem ser informadas diretamente:
43
+
44
+ ```ts
45
+ const result = await extractNFeAccessKeys("https://documents.example.com/public/danfe.pdf");
46
+ ```
47
+
48
+ Para uma URL autenticada, use a opção `requestHeaders`, disponível somente na API:
49
+
50
+ ```ts
51
+ const result = await extractNFeAccessKeys("https://documents.example.com/private/danfe.pdf", {
52
+ requestHeaders: {
53
+ Authorization: "Bearer <token>",
54
+ },
55
+ });
56
+ ```
57
+
58
+ O pacote não executa fluxos de login nem mantém um repositório de cookies. A
59
+ aplicação chamadora é responsável pelas credenciais fornecidas e pelo estado da
60
+ sessão. Prefira HTTPS sempre que enviar credenciais; cabeçalhos enviados a uma
61
+ URL HTTP inicial não são protegidos por TLS. `requestHeaders` aceita somente
62
+ valores do tipo string e não pode ser usada com um caminho local ou uma entrada
63
+ em memória. Cabeçalhos de roteamento, enquadramento, intervalo e negociação de
64
+ compressão são reservados pelo componente de download e não podem ser
65
+ sobrescritos.
66
+
67
+ ### Download de PDFs remotos
68
+
69
+ Somente URLs `http://` e `https://` são aceitas. O componente de download segue
70
+ respostas HTTP 301, 302, 303, 307 e 308, com no máximo cinco redirecionamentos.
71
+ Ele remove `Authorization`, `Cookie` e `Proxy-Authorization` quando um
72
+ redirecionamento muda a origem ou rebaixa HTTPS para HTTP. Os demais cabeçalhos
73
+ fornecidos pela aplicação chamadora são mantidos na solicitação redirecionada.
74
+
75
+ O limite `maxFileSizeBytes` (30 MiB por padrão) é aplicado tanto ao
76
+ `Content-Length` declarado quanto aos bytes recebidos durante a transferência.
77
+ `timeoutMs` abrange o download e a extração em conjunto, enquanto `signal` pode
78
+ cancelar qualquer uma das fases. É a assinatura do PDF, e não a extensão da URL
79
+ ou o tipo de mídia da resposta, que determina se o conteúdo baixado é aceito.
80
+ Falhas de download usam o código de erro `DOWNLOAD_ERROR`. Erros estruturados
81
+ não reproduzem a URL, a string de consulta nem os valores dos cabeçalhos da
82
+ solicitação.
83
+
84
+ Trate URLs remotas como entradas confiáveis. O pacote não é um filtro de SSRF:
85
+ uma URL e seus redirecionamentos podem alcançar qualquer local da rede acessível
86
+ ao processo Node.js. Aplicações que aceitam URLs fornecidas por usuários devem
87
+ aplicar sua própria política de confiança. Quando forem necessárias listas de
88
+ permissão de host, DNS/IP ou redirecionamento, faça o download com um cliente
89
+ controlado pela aplicação e envie os bytes resultantes ao extrator.
90
+
91
+ ## Resultado
92
+
93
+ A função retorna um objeto compatível com JSON para sucesso, ausência de
94
+ correspondência, entrada inválida, tempo limite e erros esperados de PDF:
95
+
96
+ ```json
97
+ {
98
+ "status": "success",
99
+ "success": true,
100
+ "precisionScore": 0.99,
101
+ "bestMatch": {
102
+ "accessKey": "35260712345678000195550010000000011123456784",
103
+ "documentType": "NFe",
104
+ "model": "55",
105
+ "format": "numeric",
106
+ "isValid": true,
107
+ "precisionScore": 0.99,
108
+ "pages": [1],
109
+ "sources": ["pdf-text"],
110
+ "occurrences": 1,
111
+ "components": {
112
+ "accessKey": "35260712345678000195550010000000011123456784",
113
+ "format": "numeric",
114
+ "stateCode": "35",
115
+ "yearMonth": "2607",
116
+ "year": "26",
117
+ "month": "07",
118
+ "issuerId": "12345678000195",
119
+ "model": "55",
120
+ "documentType": "NFe",
121
+ "series": "001",
122
+ "invoiceNumber": "000000001",
123
+ "emissionType": "1",
124
+ "numericCode": "12345678",
125
+ "checkDigit": 4
126
+ }
127
+ },
128
+ "results": [
129
+ {
130
+ "accessKey": "35260712345678000195550010000000011123456784",
131
+ "documentType": "NFe",
132
+ "model": "55",
133
+ "format": "numeric",
134
+ "isValid": true,
135
+ "precisionScore": 0.99,
136
+ "pages": [1],
137
+ "sources": ["pdf-text"],
138
+ "occurrences": 1,
139
+ "components": {
140
+ "accessKey": "35260712345678000195550010000000011123456784",
141
+ "format": "numeric",
142
+ "stateCode": "35",
143
+ "yearMonth": "2607",
144
+ "year": "26",
145
+ "month": "07",
146
+ "issuerId": "12345678000195",
147
+ "model": "55",
148
+ "documentType": "NFe",
149
+ "series": "001",
150
+ "invoiceNumber": "000000001",
151
+ "emissionType": "1",
152
+ "numericCode": "12345678",
153
+ "checkDigit": 4
154
+ }
155
+ }
156
+ ],
157
+ "metadata": {
158
+ "performance": "balanced",
159
+ "ocrMode": "fallback",
160
+ "passesRequested": 2,
161
+ "passesUsed": 0,
162
+ "pagesTotal": 1,
163
+ "pagesProcessed": 1,
164
+ "pagesRendered": 0,
165
+ "ocrPages": 0,
166
+ "fileSizeBytes": 8421,
167
+ "maxPixelsPerPage": 12000000,
168
+ "maxSourceImagePixels": 60000000,
169
+ "durationMs": 12.34,
170
+ "complete": true,
171
+ "confidenceVersion": "1.0.0"
172
+ },
173
+ "warnings": [],
174
+ "error": null
175
+ }
176
+ ```
177
+
178
+ `results` contém todas as chaves únicas e validadas de NF-e/NFC-e detectadas
179
+ pelas etapas executadas nas páginas processadas. Os perfis `fast` e `balanced`
180
+ podem ignorar etapas de maior custo depois que uma evidência válida mais barata
181
+ é encontrada; use `accurate` ou `ocr: 'always'` quando quiser verificações
182
+ cruzadas adicionais. `bestMatch` é o resultado com a maior pontuação. O
183
+ `precisionScore` do nível superior é conservador: quando várias chaves são
184
+ retornadas, ele corresponde à menor pontuação entre os resultados.
185
+
186
+ Todas as chaves permanecem como strings, portanto zeros à esquerda nunca são
187
+ perdidos.
188
+
189
+ ### O que a pontuação significa
190
+
191
+ `precisionScore` é uma pontuação determinística de confiança nas evidências,
192
+ versionada por `metadata.confidenceVersion`. Código de barras/QR, texto nativo
193
+ exato, texto reconstruído e OCR têm pontuações-base diferentes; fontes
194
+ independentes que concordam entre si aumentam a pontuação. Toda chave retornada
195
+ já passou pelas validações de formato, UF, mês, modelo 55/65, número da nota
196
+ diferente de zero, CNPJ/CPF do emitente e dígito verificador oficial por módulo 11.
197
+
198
+ A pontuação não é uma probabilidade calibrada estatisticamente. Meça precisão,
199
+ recall e falsos positivos em um corpus privado representativo antes de usar um
200
+ limiar em automações fiscais.
201
+
202
+ ## Opções
203
+
204
+ | Opção | Valores | Padrão | Significado |
205
+ | ---------------------- | ------------------------------ | -------------------- | ----------------------------------------------------------- |
206
+ | `performance` | `fast`, `balanced`, `accurate` | `balanced` | Seleciona escala de renderização, limites e política de OCR |
207
+ | `passes` | inteiro `1..5` | específico do perfil | Número de tentativas visuais distintas de renderização |
208
+ | `ocr` | `never`, `fallback`, `always` | específico do perfil | Controla o OCR local do Tesseract |
209
+ | `maxPages` | inteiro positivo | `10`, `30` ou `50` | Máximo de páginas processadas |
210
+ | `maxFileSizeBytes` | inteiro positivo | 30 MiB | Limite de tamanho da entrada |
211
+ | `maxPixelsPerPage` | inteiro positivo | específico do perfil | Limite de pixels da renderização e de imagens incorporadas |
212
+ | `maxSourceImagePixels` | inteiro positivo | específico do perfil | Limite de pixels da imagem de origem decodificada |
213
+ | `timeoutMs` | `0..3600000` | específico do perfil | Prazo do download/extração; `0` o desabilita |
214
+ | `stopAfterFirst` | booleano | `false` | Interrompe após a primeira chave validada |
215
+ | `requestHeaders` | registro de strings | nenhum | Cabeçalhos exclusivos da API para um download HTTP(S) |
216
+ | `signal` | `AbortSignal` | nenhum | Cancela o download ou a extração local |
217
+
218
+ A etapa de texto nativo sempre é executada e não conta como uma passagem visual.
219
+ Cada passagem usa uma escala ou rotação diferente; o extrator nunca repete uma
220
+ operação idêntica apenas para aumentar a confiança.
221
+
222
+ Perfis:
223
+
224
+ - `fast`: texto nativo e Code 128/QR; OCR desabilitado por padrão.
225
+ - `balanced`: duas estratégias visuais e OCR somente quando o texto/código de
226
+ barras não encontra uma chave válida.
227
+ - `accurate`: resoluções maiores, passagens opcionais com rotação e OCR local
228
+ como alternativa.
229
+
230
+ Os perfis `fast` e `balanced` ignoram o trabalho visual nas páginas que já
231
+ produziram texto válido. O perfil `accurate` ainda executa as passagens de código
232
+ de barras configuradas para fazer uma verificação cruzada independente. O OCR no
233
+ modo `fallback` só é executado nas páginas não resolvidas pelas etapas
234
+ anteriores; `ocr: 'always'` o executa de qualquer forma.
235
+
236
+ ## CLI
237
+
238
+ Após a instalação:
239
+
240
+ ```text
241
+ nf-key-extractor <file-or-url> [options]
242
+ ```
243
+
244
+ Por exemplo:
245
+
246
+ ```bash
247
+ nf-key-extractor ./nota.pdf --performance balanced --passes 2 --pretty
248
+ nf-key-extractor https://documents.example.com/public/nota.pdf --pretty
249
+ ```
250
+
251
+ O argumento posicional pode ser o caminho de um arquivo local ou uma URL pública
252
+ HTTP/HTTPS. Intencionalmente, a CLI não oferece uma opção para credenciais ou
253
+ cabeçalhos de solicitação personalizados; use a API da biblioteca com
254
+ `requestHeaders` para downloads autenticados. Evite tokens de consulta assinados
255
+ em URLs usadas na CLI, pois os argumentos do comando podem ficar visíveis no
256
+ histórico do terminal ou na lista de processos do sistema operacional.
257
+
258
+ Opções disponíveis:
259
+
260
+ ```text
261
+ --performance fast|balanced|accurate
262
+ --passes 1..5
263
+ --ocr never|fallback|always
264
+ --max-pages N
265
+ --max-file-size BYTES
266
+ --max-pixels N
267
+ --max-source-pixels N
268
+ --timeout-ms N
269
+ --first
270
+ --pretty
271
+ --help
272
+ ```
273
+
274
+ O fluxo de saída padrão contém somente JSON. O código de saída `0` significa que
275
+ uma ou mais chaves foram encontradas (ou que a ajuda foi solicitada), `2`
276
+ significa que uma varredura completa não encontrou nenhuma, e `1` indica um erro
277
+ de entrada/processamento ou uma varredura incompleta sem chave.
278
+
279
+ ## Chaves de acesso atuais e legadas
280
+
281
+ As chaves numéricas legadas continuam sendo aceitas. A expressão oficial atual é:
282
+
283
+ ```text
284
+ ^[0-9]{6}[A-Z0-9]{12}[0-9]{26}$
285
+ ```
286
+
287
+ Somente as primeiras 12 posições do identificador do emitente dentro da chave
288
+ podem ser alfanuméricas; as demais posições continuam numéricas. O cálculo do
289
+ dígito verificador mapeia cada um dos primeiros 43 caracteres para `ASCII - 48`,
290
+ aplica pesos repetidos de 2 a 9, da direita para a esquerda, e depois módulo 11.
291
+ As chaves numéricas são um subconjunto compatível do mesmo algoritmo.
292
+
293
+ O leitor de código de barras usa decodificação genérica de Code 128, portanto
294
+ aceita tanto o Code Set C legado quanto a representação híbrida atual em Code
295
+ Set C/A.
296
+
297
+ Referências oficiais:
298
+
299
+ - [MOC 7.0 - Visão Geral](https://www.confaz.fazenda.gov.br/legislacao/arquivo-manuais/moc7-visao-geral.pdf)
300
+ - [Nota Técnica Conjunta DFe 2025.001 - CNPJ alfanumérico](https://www.nfe.fazenda.gov.br/Portal/exibirArquivo.aspx?conteudo=5ZkvIZt10mQ%3D)
301
+ - [NT 2026.004 v1.01 - NF-e/NFC-e schemas](https://www.nfe.fazenda.gov.br/POrtal/exibirArquivo.aspx?AspxAutoDetectCookieSupport=1&conteudo=BTZQzgsO9Ws%3D)
302
+
303
+ ## Validação independente
304
+
305
+ O analisador e o validador fiscais são públicos e não carregam dependências de
306
+ PDF/OCR:
307
+
308
+ ```ts
309
+ import { calculateAccessKeyCheckDigit, parseAccessKey, validateAccessKey, validateIssuerIdentifier } from "nf-key-extractor";
310
+
311
+ const validation = validateAccessKey(accessKey);
312
+ ```
313
+
314
+ ## Comportamento de recursos e segurança
315
+
316
+ - Execução somente por CPU; nenhum backend de GPU é usado.
317
+ - O acesso à rede é usado apenas para baixar uma URL de entrada HTTP/HTTPS. Após
318
+ o download limitado, a extração é local; os bytes do PDF não são enviados a
319
+ um serviço de OCR nem a qualquer outro processador externo.
320
+ - Os dados de idioma do OCR são instalados localmente com o pacote e carregados
321
+ explicitamente do disco, evitando o uso padrão da CDN do Tesseract.
322
+ - Tamanho do arquivo, quantidade de páginas, pixels de imagens incorporadas e de
323
+ renderização, itens de texto, tamanho do texto, prazo e cancelamento têm
324
+ limites definidos.
325
+ - A execução de JavaScript em PDFs é desabilitada.
326
+ - As falhas esperadas são retornadas como JSON estruturado e nunca expõem
327
+ buffers nem objetos internos do PDF.
328
+
329
+ PDFs criptografados que exigem senha são informados como `PASSWORD_REQUIRED`.
330
+ PDFs ou digitalizações gravemente danificados ainda podem produzir `not_found`;
331
+ o pacote não inventa uma chave que não passe pela validação fiscal determinística.
332
+
333
+ Antes da renderização, o PDF.js ignora imagens incorporadas que excedam
334
+ `maxSourceImagePixels`. Os valores padrão dos perfis permitem digitalizações em
335
+ alta resolução sem deixar a decodificação da origem sem limites; aumente o
336
+ limite explicitamente apenas para digitalizações confiáveis e excepcionalmente
337
+ grandes.
338
+
339
+ ## Resultados de validação e benchmarks
340
+
341
+ As medições a seguir foram coletadas em 22/07/2026 com Node.js 24.11.0, Windows
342
+ x64 e um AMD Ryzen 5 5600G (12 processadores lógicos). Os tempos absolutos variam
343
+ de uma máquina para outra; use os executores para comparar alterações no mesmo
344
+ ambiente.
345
+
346
+ ### Benchmark sintético de correspondência exata
347
+
348
+ Cada cenário usa um PDF gerado com uma chave esperada conhecida. Uma execução de
349
+ aquecimento é excluída das iterações medidas.
350
+
351
+ | Cenário | Perfil / passagens | Iterações | Média | P50 | P95 | Correspondência exata |
352
+ | ------------------- | ------------------ | --------: | --------: | --------: | --------: | --------------------: |
353
+ | Texto nativo do PDF | `fast` / 1 | 20 | 3,09 ms | 3,00 ms | 3,78 ms | 100% |
354
+ | Code 128 | `balanced` / 2 | 10 | 119,44 ms | 117,72 ms | 131,64 ms | 100% |
355
+ | QR de NFC-e | `balanced` / 2 | 10 | 182,75 ms | 174,46 ms | 215,82 ms | 100% |
356
+ | OCR local | `balanced` / 1 | 3 | 724,59 ms | 723,14 ms | 762,74 ms | 100% |
357
+
358
+ ### Corpus privado de documentos reais
359
+
360
+ O extrator também foi executado localmente em 12 PDFs privados de notas fiscais
361
+ de compra (223.074 bytes no total). O diretório local `invoices/` é ignorado pelo
362
+ Git e excluído da lista de arquivos permitidos no pacote npm. O executor emite
363
+ somente métricas agregadas; nunca imprime nomes de arquivos, chaves de acesso ou
364
+ conteúdo dos documentos.
365
+
366
+ | Perfil | Execuções medidas por PDF | PDFs com chave | Erros / avisos | Média | P50 | P95 |
367
+ | ----------------------- | ------------------------: | -------------: | -------------: | ----------: | ----------: | ----------: |
368
+ | `fast`, 1 passagem | 3 | 12/12 | 0 / 0 | 8,29 ms | 8,18 ms | 9,65 ms |
369
+ | `balanced`, 2 passagens | 3 | 12/12 | 0 / 0 | 8,28 ms | 7,66 ms | 13,35 ms |
370
+ | `accurate`, 3 passagens | 1 | 12/12 | 0 / 0 | 850,43 ms | 829,81 ms | 921,52 ms |
371
+ | `accurate`, OCR forçado | 1 | 12/12 | 0 / 0 | 1.934,50 ms | 1.907,04 ms | 2.036,73 ms |
372
+
373
+ Os perfis `fast`, `balanced` e `accurate` retornaram o mesmo conjunto de chaves
374
+ validadas nos 12 documentos, com exatamente uma chave por PDF e nenhum resultado
375
+ incompleto. Todas as evidências finais desse corpus vieram do texto nativo
376
+ reconstruído do PDF. O OCR forçado processou as 12 páginas, mas não reproduziu a
377
+ chave completa de forma independente e, portanto, não aumentou a pontuação das
378
+ evidências.
379
+
380
+ Esses PDFs privados não têm uma referência correta rotulada manualmente e
381
+ verificada de forma independente. Portanto, o resultado demonstra processamento
382
+ bem-sucedido, validação fiscal determinística e concordância entre os perfis;
383
+ ele não estabelece precisão/recall estatísticos nem prova que nenhuma chave foi
384
+ ignorada. O benchmark sintético acima é o teste de correspondência exata com
385
+ chaves esperadas conhecidas.
386
+
387
+ O tarball empacotado da versão `0.1.0` também foi instalado em um projeto
388
+ consumidor temporário e limpo com npm 11.6.1. A entrada ESM, a entrada CommonJS,
389
+ o executável da CLI e as dependências locais de OCR e execução instalados foram
390
+ resolvidos corretamente; ESM, CommonJS e CLI processaram com sucesso um PDF real
391
+ privado. O projeto consumidor temporário foi removido após a verificação.
392
+
393
+ O tarball empacotado da versão `0.2.0` foi gerado e instalado em outro projeto
394
+ consumidor temporário e limpo com pnpm 10.34.5 e Node.js 24.11.0. As entradas ESM
395
+ e CommonJS, `requestHeaders`, a extração de URLs HTTP/HTTPS, as declarações
396
+ TypeScript e o executável da CLI instalados foram verificados com um PDF gerado
397
+ e respostas de rede simuladas. Nenhum endpoint ativo nem credencial de produção
398
+ foi usado.
399
+
400
+ ## Desenvolvimento
401
+
402
+ ```bash
403
+ pnpm install
404
+ pnpm typecheck
405
+ pnpm lint
406
+ pnpm test
407
+ pnpm test:real
408
+ pnpm test:coverage
409
+ pnpm security:audit
410
+ pnpm build
411
+ pnpm check
412
+ ```
413
+
414
+ A compilação gera ESM, CommonJS, declarações, mapas de código-fonte e a CLI em
415
+ `dist/`. Os fixtures de integração são sintéticos e não contêm dados reais de
416
+ contribuintes.
417
+
418
+ Execute um benchmark em JSON com:
419
+
420
+ ```bash
421
+ pnpm benchmark --scenario text --iterations 20 --performance fast --passes 1
422
+ pnpm benchmark:real
423
+ pnpm benchmark:real --include-accurate --include-ocr
424
+ ```
425
+
426
+ Consulte [`benchmarks/README.md`](benchmarks/README.md) para conhecer os cenários
427
+ e as métricas.
@@ -0,0 +1,53 @@
1
+ # Testes de desempenho (benchmarks)
2
+
3
+ O executor de benchmarks cria PDFs sintéticos em memória, verifica a
4
+ correspondência exata da chave de acesso em cada iteração e grava um documento
5
+ JSON na saída padrão.
6
+
7
+ ```bash
8
+ pnpm benchmark --scenario text --iterations 20 --performance fast --passes 1
9
+ pnpm benchmark --scenario barcode --iterations 10 --performance balanced --passes 2
10
+ pnpm benchmark --scenario qr --iterations 10 --performance balanced --passes 2
11
+ pnpm benchmark --scenario ocr --iterations 3 --performance accurate --passes 3
12
+ ```
13
+
14
+ Os valores informados incluem tempo decorrido mínimo/médio/p50/p95/máximo, tempo
15
+ de CPU, variação de RSS, informações do ambiente de execução, tamanho da entrada
16
+ e taxa de correspondência exata. A primeira extração serve como aquecimento e é
17
+ excluída das amostras.
18
+
19
+ Não use os tempos absolutos de uma única máquina como critério de CI. Em vez
20
+ disso, compare execuções repetidas no mesmo hardware e monitore regressões
21
+ relativas.
22
+
23
+ ## Corpus privado de notas fiscais locais
24
+
25
+ `real-corpus.ts` localiza recursivamente os PDFs em um diretório local, executa o
26
+ aquecimento de cada perfil, mede a latência por documento e compara os conjuntos
27
+ de chaves validadas retornados por todos os perfis. Sua saída JSON contém somente
28
+ dados agregados: nunca inclui chaves de acesso, conteúdo dos documentos, caminhos
29
+ ou nomes de arquivos.
30
+
31
+ ```bash
32
+ pnpm exec tsx benchmarks/real-corpus.ts
33
+ pnpm exec tsx benchmarks/real-corpus.ts --input-dir invoices --iterations 3 --warmup-iterations 1
34
+ pnpm exec tsx benchmarks/real-corpus.ts --include-accurate
35
+ pnpm exec tsx benchmarks/real-corpus.ts --include-accurate --include-ocr
36
+ pnpm exec tsx benchmarks/real-corpus.ts --require-key-per-document
37
+ ```
38
+
39
+ Os perfis `fast` e `balanced` sempre são executados. `--include-accurate`
40
+ adiciona o modo `accurate` com OCR como alternativa, enquanto `--include-ocr`
41
+ adiciona um perfil `accurate` que força o mecanismo de OCR local incluído no
42
+ pacote. O OCR pode ser consideravelmente mais lento, por isso é opcional.
43
+
44
+ O relatório inclui o tamanho do corpus, dados agregados de tempo de execução e
45
+ memória, contagens de sucesso por perfil, latência média/p50/p95, contagens de
46
+ documentos por categoria de origem não exclusiva, estabilidade entre iterações
47
+ e concordância exata dos conjuntos de chaves. O executor termina com status
48
+ diferente de zero em caso de erros de extração, processamento incompleto,
49
+ resultados não determinísticos ou divergências entre perfis. Um resultado
50
+ `not_found` válido é incluído nas contagens de sucesso, mas não é tratado como
51
+ erro de processamento, a menos que `--require-key-per-document` esteja definido.
52
+ A concordância entre perfis mede a consistência dos modos de extração; sem uma
53
+ referência correta verificada de forma independente, ela não mede a acurácia.