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

@chad3814/par2

v2.0.0

Published

PAR2 (Parity Volume Set Specification 2.0) verification and repair — namespace reserved, not yet implemented

Readme

@chad3814/par2

PAR2 (Parity Volume Set Specification 2.0) parsing and verification.

Status: 2.0.0. Zero runtime dependencies — node:crypto for MD5, node:zlib for CRC32.

Verification only. There is no repair. Reed-Solomon reconstruction is a much larger job with a narrower payoff, and everything below is useful without it. See "What repair would add".

import { parsePar2, verifyFile } from '@chad3814/par2';

const set = parsePar2(await readFile('release.par2'));
// → sliceSize, and for every protected file: name, length, md5, per-slice checksums

for (const file of set.files) {
  const result = verifyFile(file, set.sliceSize, await readFile(file.name));
  console.log(file.name, result.ok, result.damagedSlices);
}

Why it earns its place

It is the only authoritative filename. An NZB has no filename field, and on an obfuscated post the yEnc =ybegin name= is a random string that differs on every article. PAR2's FileDesc packets carry the real name, the real length and an MD5 — and the index .par2 that holds them is a single article. On the release this was built against: 5,396 bytes.

It is an independent check on the whole stack. Every article is already CRC-checked on arrival, so transit corruption is caught. That says nothing about whether the assembly was right. A reassembled file matching the set's MD5 proves every offset, every =ypart placement check and every join was correct, against an authority nobody here wrote.

Verifying needs no parity. Recovery slices exist to repair. The critical packets — Main, FileDesc, IFSC, Creator — are duplicated into every volume and are all verification needs, so this never fetches the hundreds of megabytes of recovery data. On the audited release the parity volumes are 384 MiB and the index is 5 KB.

What it does

  • Scans for packets by magic, not by offset, and validates each one's MD5. That is what lets a damaged, truncated or interleaved volume still yield everything that survived — and what makes scanning safe, since without the hash any eight bytes of payload spelling PAR2\0PKT would be read as a header.
  • Merges volumes. Critical packets repeat across every volume; they are keyed and merged rather than appended.
  • Ignores packets from another set. Identity comes from the first Main packet, because that is what defines a set — not from whichever packet happened to be at the front of the input.
  • Verifies streamed, so a 7.8 GiB file is checked without being held in memory. Length, whole-file MD5, and per-slice MD5 + CRC32 that say which slices are wrong rather than only that something is.
  • Zero-pads the final slice, which the spec requires and which a naive implementation forgets — producing a phantom mismatch on the last slice of every file whose length is not an exact multiple of the slice size, which is most of them.

What repair would add

The recovery slices are parsed and counted but their data is discarded. Using them means Reed-Solomon over GF(2^16): log/antilog tables, matrix inversion, and a multiply-accumulate pass over every present slice for each missing one. On the audited release that is roughly 7.3 GiB of field arithmetic to reconstruct a single 32 MiB slice.

Worth knowing before assuming repair is the answer to a missing article: on that release the recovery set protects 2 files, and the one article that had actually expired — the .nfo — was not one of them. par2 would not have saved it. Fetching from a second provider often solves more real failures per hour of work than reimplementing Reed-Solomon.

Slices are not articles

A slice is the PAR2 unit; an article is the Usenet unit. On the audited release a slice is 32 MiB and an article is 4 MiB, so one lost article destroys a whole slice. Several losses inside the same slice still cost only that one slice, so clustered damage is cheap and scattered damage is what exhausts a set's budget.

Testing

38 unit tests over packets built byte by byte from the specification, with offsets written as literals rather than taken from this package's own constants — a fixture that shares its layout with the parser would agree with a wrong parser. Hashes come from node:crypto and node:zlib.

Mutation-tested: skipping the final slice's zero padding, never checking a packet's MD5, resuming a scan at a corrupted length instead of one byte on, mixing packets from a foreign set, taking set identity from the first packet rather than the first Main, and dropping the packet-size cost cap each fail at least one test. Three of those survived a first pass and exposed real gaps: two tests appended a foreign packet where only a leading one bites, and the size cap had no test at all until the limit was made injectable.

Verified against a real release end to end: the index .par2 fetched as one article, a downloaded JPEG confirmed against the set's MD5, and a single flipped byte reported as one damaged slice.