npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tiss-hash

v0.1.0

Published

Hash MD5 do epílogo XML TISS/ANS (Padrão TISS). Reverse-engineered, validado contra goldens reais privados + vetores sintéticos.

Readme

tiss-hash (Node.js)

Calcula a "impressão digital" do trecho final de um documento TISS/ANS. Antes do código, os termos essenciais:

  • XML: formato de arquivo de texto que organiza dados em etiquetas (tags) aninhadas, como caixas dentro de caixas. O Padrão TISS é o XML que operadoras de saúde e consultórios usam no Brasil para trocar dados de atendimento (regulamentado pela Agência Nacional de Saúde Suplementar, a ANS).
  • Hash: sequência curta e fixa de caracteres calculada a partir de um texto, como uma impressão digital. Mude uma letra, o hash muda inteiro.
  • MD5: a receita (algoritmo) que gera o hash; sempre 32 caracteres hexadecimais (0-9 e a-f).
  • Epílogo: a parte final do documento TISS, a etiqueta <ans:hash>, onde o hash precisa ser gravado.
  • Byte: a menor unidade de dado do computador; um arquivo de texto é uma fila de bytes.

Em uma frase: você passa os bytes de um XML TISS e recebe os 32 caracteres do hash. Este é o port Node.js ("port" = a mesma lib reescrita em outra linguagem) da biblioteca lib_hash_ans. Outras linguagens (Python, Rust, C, C++, PHP, etc.) seguem o mesmo contrato e os mesmos vetores de conformidade. Para entender o problema que a lib resolve, veja docs/USAGE.md (guia de uso) e docs/ARCHITECTURE.md (conceitos e visão geral).

  • Status: alpha. 20/20 vetores sintéticos PASS (18 positivos + 2 negativos) em conformance/vectors.json.
  • Licença: MIT.
  • Engines: Node.js >=20 (testado em 20 LTS e 22 LTS).
  • Dependência runtime única: @xmldom/xmldom (DOM puro, pure-JS, sem deps nativas).

Antes de começar: instalar o Node.js

Node.js é o ambiente que executa JavaScript fora do navegador. Ele já vem com o npm, o gerenciador que baixa e instala bibliotecas (dependências).

  • Baixe e instale pelo site oficial: https://nodejs.org/ (escolha a versão "LTS"; precisa da 20 ou mais nova).
  • Confira a instalação:
node --version
npm --version

Instalação

Uma dependência é uma biblioteca de terceiros que o seu código usa; o npm a baixa e instala. O comando abaixo adiciona esta lib ao seu projeto:

npm install tiss-hash

A publicação no npm registry (o repositório oficial de pacotes Node.js) ainda não foi feita. Por enquanto, instale diretamente a partir do checkout do repositório (a pasta que você baixou com git clone):

npm install /caminho/para/lib_hash_ans/langs/node

Quickstart

ESM (recomendado)

import { hashTiss, hashTissFile } from 'tiss-hash';
import { readFileSync } from 'node:fs';

// A partir de bytes (sincrono)
const md5 = hashTiss(readFileSync('lote.xml'));
console.log(md5); // ex.: '3aa0c578c95cdb861a125f480a8a4de5'

// A partir de um caminho de arquivo (assincrono)
const md5b = await hashTissFile('lote.xml');

CommonJS

const { hashTiss, hashTissFile } = require('tiss-hash');
const { readFileSync } = require('node:fs');

const md5 = hashTiss(readFileSync('lote.xml'));

Tratamento de erro

import { hashTiss, InvalidTissXmlError } from 'tiss-hash';

try {
  hashTiss(Buffer.from('<nao-eh-xml'));
} catch (err) {
  if (err instanceof InvalidTissXmlError) {
    console.error('XML rejeitado:', err.message);
  } else {
    throw err;
  }
}

API

| Símbolo | Tipo | Descrição | | --- | --- | --- | | hashTiss(xmlBytes) | (Uint8Array \| Buffer) => string | Hash MD5 (hex, 32 chars minúsculo) a partir dos bytes do XML. Sincrono. | | hashTissFile(filePath) | (string \| URL) => Promise<string> | Atalho assíncrono: lê o arquivo e calcula o hash. | | InvalidTissXmlError | class extends Error | Lançada quando o XML é mal-formado ou rejeitado pelo parser. | | TISS_NAMESPACE | string | URI do namespace TISS: http://www.ans.gov.br/padroes/tiss/schemas. |

Algoritmo

Resumo do que hashTiss faz:

  1. Decodifica os bytes do XML respeitando a declaração encoding="...":
    • iso-8859-1 (padrão TISS): mapeia byte para codepoint (latin1) e reescreve a declaração para utf-8 antes de passar ao parser.
    • utf-8: usa direto. Strippa BOM UTF-8 se presente.
  2. Parseia o XML com @xmldom/xmldom (DOM W3C compliant, mantém comentários em childNodes).
  3. Localiza o <ans:hash> (por namespace URI, não por prefixo) e marca-o para ser zerado. Documento sem <ans:hash> é válido; com múltiplos <ans:hash> é rejeitado (erro).
  4. Caminha a árvore em ordem de documento (depth-first, pre-order). Para cada nó-folha, isto é, Element ou Comment cujos filhos NÃO contêm Element/Comment/ProcessingInstruction, concatena textContent ao buffer. O <ans:hash> contribui "".
  5. Calcula MD5 sobre os bytes UTF-8 da string concatenada.
  6. Devolve o hexdigest() em minúsculo (32 caracteres).

Atenção: encoding do MD5 é UTF-8, não ISO-8859-1

O manual TISS (Componente Organizacional nov/2025, pág 53, item 146) diz "O encoding a ser utilizado será sempre o ISO-8859-1". Essa frase é ambígua e foi historicamente mal interpretada. Refere-se ao encoding do arquivo XML, não dos bytes que alimentam o MD5.

Na prática (validada contra goldens reais privados, fora do repo, além dos vetores sintéticos públicos), os valores extraídos do XML são re-encodados em UTF-8 antes do MD5. Implementações que aplicam MD5 sobre bytes ISO-8859-1 produzem hash diferente e errado.

Spec completa: docs/SPEC.md. Catálogo das 15 decisões canônicas (CDATA, entidades, atributos, comentários, etc.): conformance/AMBIGUITY_NOTES.md.

Decisão de parser: @xmldom/xmldom

Avaliadas três opções:

| Parser | Decisão | Motivo | | --- | --- | --- | | @xmldom/xmldom | escolhido | DOM puro, pure-JS, sem deps nativas. Semântica W3C DOM próxima do ElementTree/lxml do Python. Mantém nós Comment (nodeType 8) em childNodes por padrão, essencial pra reproduzir a ambiguidade #2 da referência (comentários XML ENTRAM no concat). | | fast-xml-parser | descartado | Converte XML para objeto JS, perde noção de ordem e o conceito de "elemento-folha" sem reconstrução manual. | | sax / ltx | descartado | Streaming/SAX exigem reconstruir manualmente a árvore. Sem ganho real (XMLs TISS < 10 MB). |

Conformidade

Esta lib passa os 20 vetores sintéticos (18 positivos + 2 negativos) em conformance/vectors.json, cobrindo:

  • 18 positivos: mínimo, acentuação (UTF-8 vs ISO-8859-1), campos vazios, CR/LF, múltiplas guias, entidades XML, entidades numéricas, CDATA, comentários, atributos, namespaces variados, namespace default, sem <ans:hash> (válido), whitespace puro, zeros à esquerda, símbolos ISO-8859-1, performance (~600 KB), BOM UTF-8.
  • 2 negativos (esperam erro): syn_multi_hash.xml (múltiplos <ans:hash>) e syn_utf16.xml (UTF-16 fora de escopo).

A lista canônica vive em conformance/vectors.json.

Cada vetor é um par "arquivo de entrada -> hash esperado": positivo deve produzir um hash, negativo deve ser rejeitado (a lib precisa recusar o arquivo, em vez de inventar um hash).

Rodar os testes localmente, a partir desta pasta:

npm install   # baixa as dependências
npm test      # roda os 20 vetores de conformidade

Saída esperada: 20 vetores passando (incluindo os 2 negativos que devem lançar erro) + asserts de API auxiliares.

Limitações conhecidas

  • Múltiplos <ans:hash> no mesmo documento: rejeitado (lança InvalidTissXmlError). Cenário patológico, não previsto pelo padrão TISS; falhar é mais seguro que adivinhar qual zerar.
  • Encodings suportados: ISO-8859-1 e UTF-8. UTF-16/UTF-32 são rejeitados (fora de escopo).
  • Bytes inválidos no encoding declarado: comportamento não fixado pela suite. O parser tipicamente substitui ou rejeita; a lib não tenta recuperar.

Dependências e licenças

Dependência de runtime única (MD5 vem do node:crypto, sem deps nativas):

| Dependência | Licença | Uso | | --- | --- | --- | | @xmldom/xmldom | MIT | parser DOM (pure-JS) |

Atribuição consolidada de todos os ports em THIRD_PARTY_LICENSES.md na raiz do repo.

Licença

MIT Copyright (c) 2026 Petrus Silva Costa

Ver também