npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details


  • User packages



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.


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 🙏

© 2021 – Pkg Stats / Ryan Hefner




Sign and Encrypt Data




Secure Chest

Build Status Test Coverage Dependabot Status Dependencies NPM Downloads Semantic-Release Gardener

Web-safe Encryption and Signing of Data

Use Case

Intended for storing data with untrusted party. Useful when storing data on server is expensive, forbidden, inconvenient or impossible.

Data is first signed and then, together with a timestamp, encrypted into a "chest" using a secret. Data can be extracted again and checked for consistency and freshness using the same secret.

Encoded Data is Url Safe and satisfies the regular expression ^[A-Za-z0-9\-_]+$. Internally Gzip is used when this shortens the tokens.

Getting Started

$ npm i --save secure-chest

Below is an example flow that allows users to be signed in without persisting any information on the server.

const { Chester } = require('secure-chest');
const { DecryptionExpiredError } = require('secure-chest').errors;

const chester = Chester('SECRET-ENCRYPTION-KEY', {
  name: 'facebook-auth',
  maxAgeInSec: 60 * 60 // require re-auth every hour

// ... facebook oauth flow ...

const token = getFacebookSessionToken();
const chest = chester.lockObj({ token });

// ... store chest with client ...

// ... time passes ...

// .. client makes request and provides chest ...

try {
  if (isValidFacebookUser(chester.unlockObj(chest))) {
    // welcome back
} catch (e) {
  if (e instanceof DecryptionExpiredError) {
    // ... re-authenticate with facebook ...

Or to create an unsubscribe link without storing information on the server one could use it as follows.

const { Chester } = require('secure-chest');
const { DecryptionExpiredError } = require('secure-chest').errors;

const chester = Chester('SECRET-ENCRYPTION-KEY', {
  name: 'email-unsubscribe',
  maxAgeInSec: 60 * 60 * 24 * 90 // link is valid for 90 days

// could also include hashed password-salt if unsubscribe links should be invalidated when password changes
const unsubscribeLink = `${chester.lockObj({ userId, userEmail })}`;

// ... generate and send email to user ...

// ... user clicks unsubscribe link ...

const token = getQueryParam('token');

try {
  const info = chester.unlockObj(token);
  const user = findUser(info.userId, info.userEmail);
  if (user) {
  } else {
    // user does not exist or email address was changed
} catch (e) {
  if (e instanceof DecryptionExpiredError) {
    // unsubscribe link has expired


Exposes main functionality.

Uses GZip internally when this shortens the output.



Type: string or Buffer

Secret used to encrypt data. If string is provided it is converted into Buffer internally using provided encoding.


Type: string Default: default

Name of this Chester. A Chester can not open chests if Chester with different name but same secret locked them. Ease-of-life, so one can use same secret for different Chester.

Internally name is merged with provided secret and passed into underlying Crypter.


Type: constants.ENCODING Default: constants.ENCODING.utf8

Encoding used to convert between strings and Buffers. For most cases utf8 is suitable.


Type: number Default: 1514764800

Used to delay year 2038 problem. Should never be changed.

Necessary since timestamp is stored as 4 bytes.


Type: number Default: 60

Maximum age in seconds before chest expires and DecryptionExpiredError is thrown when trying to unlock it.

When value is changed it is automatically changed for all previously created chests, since chests only store a timestamp.


Type: constant.GZIP_MODE Default: constant.GZIP_MODE.AUTO

Overwrite gzip mode. By default gzip mode is only used when output is shortened.


Type: zlib.constants.* Default: zlib.constants.Z_BEST_COMPRESSION

Desired zlib compression.


See Cryper below


See Cryper below


Exported from top level errors.


General Encryption Error that all Encryption Errors inherit from.


Thrown from lockObj when JSON.stringify fails.


General Decryption Error that all Decryption Errors inherit from.


Provided data can not be decrypted. Can be indication for invalid data or incorrect secret or name.


Data was decrypted successfully, but signature did not match. Can also indicate invalid data or an incorrect secret or name.

Also thrown when context was changed.


The chest is not valid yet. This usually only happens when the zeroTime is changed.


The chest has expired. Not risen when expire option is set to false.


The gzip content of the chest is invalid. This should never happen.


Thrown from unlockObj when JSON.parse fails.



lock(treasure, options = {})

Create and "lock" new chest. Takes data to encrypt as first argument and optional options as second argument.


Wraps lock, and JSON.stringify is applied to first argument. On failure EncryptionJsonError is thrown.


unlock(chest, options = {})

Unlock a chest and returns data. Takes data to decrypt as first argument and options as optional second argument.

This method can throw various errors (see section).


Wraps unlock, and JSON.parse is applied to return value. On failure DecryptionJsonError is thrown.



Type: String[] Default: []

When unlocking chest where contexts have been provided to lock it, unlocking requires the contexts to be identical. Useful for IP address or User-Agent if changes should invalidate the chest.


Type: Boolean Default: true

When set to false the DecryptionExpiredError is never risen.


const Chester = require('secure-chest').Chester;

const chester = Chester('SECRET-ENCRYPTION-KEY');
const data = 'Some Text';
const chest = chester.lock(data);
// => "Some Text"


Used to encrypt and decrypt data using aes-256-cbc with 16 bit random IV by default.

Deals only with Buffers and produced web-safe base64 and hence is encoding independent.

Important: Errors are not explicitly handled.




Takes a Buffer and encrypts it as a web-safe base64 encoded string.



Takes a web-safe base64 encoded string and decrypts it into a Buffer.



Type: Buffer

Secret used to encrypt data. Internally this gets hashed.


Type: string Default: aes-256-cbc

Defines encryption algorithm. IV length must be compatible.


Type: number Default: 16

Defines length of IV. Must be compatible with encryption.


const crypto = require('crypto');
const Crypter = require('secure-chest').Crypter;

const crypter = Crypter(crypto.randomBytes(64));

const data = crypto.randomBytes(64);
const encrypted = crypter.encrypt(data); // non-deterministic due to IV
const decrypted = crypter.decrypt(encrypted);, decrypted);
// => 0




Defines gzip mode used internally.


Values utf8, ascii, latin1, binary

Defines encoding for string to buffer conversions.

Utility Functions

The functions toUrlSafeBase64 and fromUrlSafeBase64 are exposed.



Implementation Notes

This project is considered complete and won't see any major features or changes.

Input values are heavily checked and TypeError is raised if invalid.

Signature Observations

By default GZip is only used when this shortens the output. One bit in the signature indicates if gzip is used and hence only len - 1 bits are the "true" signature.

Acceptable since signature is 16 bytes and this doesn't increase collisions significantly.