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

@boletix/bu-qr

v0.1.0

Published

Parse and assemble the QR codes printed on Brazilian boletins de urna (TSE, VRQR 6.0).

Readme

@boletix/bu-qr

Parse the QR codes printed on Brazilian boletins de urna (BU), the per-section vote tallies every voting machine prints when the polls close. Zero runtime dependencies; runs in browsers, Node, Deno and Cloudflare Workers (hashing uses WebCrypto).

Supports the VRQR 6.0 layout from the TSE Manual do QR Code no Boletim de Urna (2026 elections). Older layouts are rejected with UNSUPPORTED_VERSION.

To verify the signature and the urna certificate, use @boletix/bu-verify.

Install

npm install @boletix/bu-qr

Usage

import { assembleBu, assembleCertificate, parseQr, type BuQr } from "@boletix/bu-qr";

// One string per scanned QR code, in any order.
const parsed = scanned.map(parseQr);
const parts = parsed.filter((qr): qr is BuQr => qr.kind === "bu");
const certs = parsed.filter((qr) => qr.kind === "cert");

// Checks every printed HASH against the content (HASH_MISMATCH otherwise), then decodes.
const { bu, hash, assi } = await assembleBu(parts);
bu.header.municipio; // 29688
bu.cargos[0]?.candidates; // { "13": 120, "22": 98, ... }
// `hash` covers the whole BU; `assi` (bytes) is the urna's signature over it.

// The urna's X.509 certificate (DER), printed in the QRCE codes.
const der = assembleCertificate(certs);

Every failure throws a BuError with a stable code (BuErrorCode) and a human-readable detail:

import { BuError } from "@boletix/bu-qr";

try {
  parseQr(raw);
} catch (error) {
  if (error instanceof BuError && error.code === "UNSUPPORTED_VERSION") {
    // a BU from another election
  }
}

API

| Export | Description | | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | parseQr(raw) | Decodes one QR: a BU part (kind: "bu") or a certificate part (kind: "cert"). | | assembleBu(parts) | Async. Joins all parts of one BU (any order), checks every HASH against the recomputed chain (HASH_MISMATCH), decodes header and cargos and checks that the printed totals add up (TOTALS_INCONSISTENT). Returns { bu, hash, assi }. | | chainHashes(contents) | Low-level: the SHA-512 HASH chain over parts' content, in index order. Async (WebCrypto), uppercase hex. Use it to print a BU; assembleBu already checks it when reading one. | | assembleCertificate(certs) | Joins the QRCE parts of one urna into the certificate DER (accepts DER or PEM inside CERT). | | bytesToHex(bytes), hexToBytes(hex) | Lowercase hex encoding; strict decoding (either case) that throws INVALID_FIELD on odd length or non-hex characters. | | BuError, BuErrorCode | Thrown for QRs that cannot be read or assembled. Signature and certificate failures are @boletix/bu-verify's VerificationError. | | MAX_QR_CHARS, MAX_QR_CODES | Limits: 1100 characters per QR (manual), 32 QRs per BU (a safety bound, since the manual sets none). | | Bu, BuCargo, BuQr, CertQr, ParsedQr | Types. Field names follow the manual; doc comments note the QR key each field comes from. |

Format rules

These were verified against real 2026 BUs, not just the manual.

  • Tokens. A QR is KEY:VALUE tokens separated by single spaces, in the QR alphanumeric alphabet (0-9 A-Z $%*+-./: and space). Values split on the first colon only (HICA:1:8027).
  • Header. QRBU:<i>:<n> VRQR:<version> starts every BU part; QRCE:<i>:<n> IDUE MDUE CERT every certificate part. Which header keys a BU may carry depends on ORIG (VOTA, RED, SA).
  • HASH. Each part ends in HASH:<128 hex>, optionally followed by ASSI:<hex> on the last part only. A part's content is the exact text between the QRBU/VRQR header and HASH:.
  • HASH chain. HASH₁ = SHA-512(content₁), and for each following part HASHᵢ = SHA-512(textᵢ₋₁ + " HASH:" + HASHᵢ₋₁ + " " + contentᵢ), where textᵢ is the string hashed for part i. Only the last HASH is signed, and it covers the whole BU.
  • Certificate. CERT is hex. Urnas from 2013/2015 print the DER directly; 2020 and later print PEM text, which assembleCertificate decodes to DER.

License

BSD-3-Clause