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

unrarit

v0.0.6

Published

unrar library for JavaScript

Readme

unrarit

unrar library for browser and node based JavaScript

Build and Deploy [Live Tests]

How to use

Live Example: https://jsfiddle.net/greggman/s5e0g96c/

import { unrar } from 'unrarit';

async function readFiles(url) {
  const { entries } = await unrar(url);

  // print all entries and their sizes
  for (const [name, entry] in Object.entries(entries)) {
    console.log(name, entry.size);
  }

  // read an entry as an ArrayBuffer
  const arrayBuffer = await entries['path/to/file'].arrayBuffer();

  // read an entry as a blob and tag it with mime type 'image/png'
  const blob = await entries['path/to/otherFile'].blob('image/png');
}

You can also pass a Blob, ArrayBuffer, SharedArrayBuffer, TypedArray, or your own Reader

For using without a builder/bundler grab unrarit.umd.js or unrarit.module.js from the dist folder and include with

import * as unrarit from `./unrarit.module.js`;

or

<script src="unrarit.umd.js"></script>

or vs CDN

import * as unrarit from 'https://unpkg.com/[email protected]/dist/unrarit.module.js';

or

<script src="https://unpkg.com/[email protected]/dist/unrarit.umd.js"></script>

Node

For node you need to make your own Reader or pass in an ArrayBuffer, SharedArrayBuffer, or TypedArray.

Load a file as an ArrayBuffer

import { unrar } from 'unrarit';
import { promises as fsPromises } from 'fs'

async function readFiles(filename) {
  const buf = await fsPromises.readFile(filename);
  const { rar, entries } = await unrar(buf);
  ... (see code above)
}

You can also pass your own reader. Here's 2 examples. This first one is stateless. That means there is never anything to clean up. But, it has the overhead of opening the source file once for each time you get the contents of an entry. I have no idea what the overhead of that is.

import { unrar } from 'unrarit';
import { promises as fsPromises } from 'fs'

class StatelessFileReader {
  constructor(filename) {
    this.filename = filename;
  }
  async getLength() {
    if (this.length === undefined) {
      const stat = await fsPromises.stat(this.filename);
      this.length = stat.size;
    }
    return this.length;
  }
  async read(offset, length) {
    const fh = await fsPromises.open(this.filename);
    const data = new Uint8Array(length);
    await fh.read(data, 0, length, offset);
    await fh.close();
    return data;
  }
}

async function readFiles(filename) {
  const reader = new StatelessFileReader(filename);
  const { rar, entries } = await unrar(reader);
  ... (see code above)
}

Here's also an example of one that only opens the file a single time but that means the file stays open until you manually call close.

class FileReader {
  constructor(filename) {
    this.fhp = fsPromises.open(filename);
  }
  async close() {
    const fh = await this.fhp;
    await fh.close();
  }
  async getLength() {
    if (this.length === undefined) {
      const fh = await this.fhp;
      const stat = await fh.stat();
      this.length = stat.size;
    }
    return this.length;
  }
  async read(offset, length) {
    const fh = await this.fhp;
    const data = new Uint8Array(length);
    await fh.read(data, 0, length, offset);
    return data;
  }
}

async function doStuff() {
  // ...

  const reader = new FileReader(filename);
  const { rar, entries } = await unrar(reader);

  // ... do stuff with entries ...

  // you must call reader.close for the file to close
  await reader.close();
}

API

import { unrarit, unraritRaw, cleanup } from 'unrarit';

unrar, unrarRaw

async unrar(url: string): RarInfo
async unrar(src: Blob): RarInfo
async unrar(src: TypedArray): RarInfo
async unrar(src: ArrayBuffer): RarInfo
async unrar(src: Reader): RarInfo

async unrarRaw(url: string): RarIInfoRaw
async unrarRaw(src: Blob): RarIInfoRaw
async unrarRaw(src: TypedArray): RarIInfoRaw
async unrarRaw(src: ArrayBuffer): RarIInfoRaw
async unrarRaw(src: Reader): RarIInfoRaw

unrar and unrarRaw are async functions that take a url, Blob, TypedArray, or ArrayBuffer or a Reader. Both functions return an object with fields rar and entries. The difference is with unrar the entries is an object mapping filenames to RarEntrys where as unrarRaw it's an array of RarEntrys. The reason to use unrarRaw over unrar is if the filenames are not utf8 then the library can't make an object from the names. In that case you get an array of entries, use entry.nameBytes and decode the names as you please.

type RarInfo = {
  rar: Rar,
  entries: {[key: string]: RarEntry},
};
type RarIInfoRaw = {
  rar: Rar,
  entries: [RarEntry],
};
class Rar {
  comment: string,           // the comment for the rar file
  commentBytes: Uint8Array,  // the raw data for comment, see nameBytes
}
class RarEntry {
  async blob(type?: string): Blob,  // returns a Blob for this entry
                                    //  (optional type as in 'image/jpeg')
  async arrayBuffer(): ArrayBuffer, // returns an ArrayBuffer for this entry
  async text(): string,             // returns text, assumes the text is valid utf8.
                                    // If you want more options decode arrayBuffer yourself
  async json(): any,                // returns text with JSON.parse called on it.
                                    // If you want more options decode arrayBuffer yourself
  name: string,                     // name of entry
  nameBytes: Uint8Array,            // raw name of entry (see notes)
  size: number,                     // size in bytes
  compressedSize: number,           // size before decompressing
  comment: string,                  // the comment for this entry
  commentBytes: Uint8Array,         // the raw comment for this entry
  lastModDate: Date,                // a Date
  isDirectory: bool,                // True if directory
  encrypted: bool,                  // True if encrypted
}
interface Reader {
  async getLength(): number,
  async read(offset, size): Uint8Array,
}

cleanup

This releases the WASM module.

cleanup()

Notes:

Caching

If you ask for the same entry twice it will be read twice and decompressed twice. If you want to cache entires implement that at a level above unrarit

Streaming

I don't how much of a win this is but, If your server supports http range requests you can do this.

import { unrar, HTTPRangeReader } from 'unrarit';

async function readFiles(url) {
  const reader = new HTTPRangeReader(url);
  const { rar, entries } = await unrar(reader);
  // ... access the entries as normal
}

Rar files to do not have a table-of-contents. Instead they just have [header][data][header][data][header][data] So, normally, unrarit would download the entire rar file and read each header. If you use HTTPRangeReader then instead of downloading the entire file it can just read each header. If you have a 10 meg rar that contains ten 1 meg files and you only need one of those 1 meg files this should be a win as it will only need to download a few k plus 1 meg. On the other hand it has to make one request per header so if you have 1 meg rar with 1000 one k files that would be 1000 requests to read the headers.

Special headers and options for network requests

The library takes a URL but there are no options for cors, or credentials etc. If you need that pass in a Blob or ArrayBuffer you fetched yourself.

import { unrar } from 'unrarit';

...

const req = await fetch(url, { mode: 'cors' });
const blob = await req.blob();
const { entries } = await unrar(blob);

ArrayBuffer and SharedArrayBuffer caveats

If you pass in an ArrayBuffer or SharedArrayBuffer you need to keep the data unchanged until you're finished using the data. The library doesn't make a copy, it uses the buffer directly.

Handling giant entries

There is no way for the library to know what "too large" means to you. The simple way to handle entries that are too large is to check their size before asking for their content.

  const kMaxSize = 1024*1024*1024*2;  // 2gig
  if (entry.size > kMaxSize) {
    throw new Error('this entry is larger than your max supported size');
  }
  const data = await entry.arrayBuffer();
  ...

Encrypted, Password protected Files

unrarit does not currently support encrypted rar files and will throw if you try to get the data for one. Put it on the TODO list 😅

Live Browser Tests

https://greggman.github.io/unrarit/test/

License

MIT except for the UnRar code who's license is here